package com.wang.lotterysystem.service.manager.impl;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.wang.lotterysystem.common.errorcode.ServiceErrorCodeConstants;
import com.wang.lotterysystem.common.exception.ServiceException;
import com.wang.lotterysystem.service.ActivityService;
import com.wang.lotterysystem.service.dto.activity.ActivityStatusConvertDTO;
import com.wang.lotterysystem.service.manager.ActivityStatusManager;
import com.wang.lotterysystem.service.manager.operator.AbstractActivityOperator;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;

@Service
@Slf4j
public class ActivityStatusManagerImpl implements ActivityStatusManager {

    /**
     * 注入 AbstractActivityOperator 的所有子类实例
     *      Map的键(String) --> 默认为Bean的名称 (如prizeOperator)
     *      Map的值(AbstractActivityOperator) --> 对应的AbstractActivityOperator子类实例
     */
    @Autowired
    private Map<String, AbstractActivityOperator> operatorMap;

    @Autowired
    private ActivityService activityService;

    /**
     * 转换状态
     * (1)先处理: 人员,奖品 状态
     * (2)后处理: 活动 状态
     * (3)更新缓存
     *
     * @param activityStatusConvertDTO 状态转换参数
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void handlerEvent(ActivityStatusConvertDTO activityStatusConvertDTO) throws JsonProcessingException {

        //先判空
        if (operatorMap == null || operatorMap.isEmpty()){
            //如果状态扭转类的实例为空
            //打印日志并返回
            log.warn("AbstractActivityOperatorMap 为空");
            return;
        }

        //扭转活动状态

        //定义一个Boolean类型的变量update, 表示是否进行了修改
        Boolean update = false;

        //用Map类型的变量currMap接收当前状态扭转类的实例
        Map<String, AbstractActivityOperator> currMap = new HashMap<>(operatorMap);

        // (1)先扭转奖品和用户的状态
        update = processStatusConversion(activityStatusConvertDTO,currMap,1);

        // (2)奖品和用户状态都扭转完成之后,扭转活动的状态
        //注意这里的逻辑: 奖品/用户状态 或 活动状态 只要扭转了一个, update都为true
        update = processStatusConversion(activityStatusConvertDTO,currMap,2) || update;

        if (update){
            //如果进行了修改 --> 更新缓存
            activityService.cacheActivity(activityStatusConvertDTO.getActivityId());
        }
    }

    /**
     * 状态回滚
     * @param convertDTO 活动状态参数
     */
    @Override
    public void rollback(ActivityStatusConvertDTO convertDTO) throws JsonProcessingException {

        if (convertDTO == null){
            log.warn("无需回滚状态");
            return;
        }

        //获取所有需要回滚的类
        Collection<AbstractActivityOperator> values = operatorMap.values();

        //回滚所有operator实现类的状态
        for (AbstractActivityOperator operator : values) {
            operator.convertStatus(convertDTO);
        }

        //刷新缓存中的数据
        activityService.cacheActivity(convertDTO.getActivityId());
    }


    /**
     * 状态扭转方法
     * @param convertActivityStatusDTO 状态扭转参数
     * @param currMap 当前状态扭转类的实例Map
     * @param sequence 扭转次序
     * @return 是否扭转成功
     */
    private Boolean processStatusConversion(ActivityStatusConvertDTO convertActivityStatusDTO,
                                            Map<String, AbstractActivityOperator> currMap,
                                            int sequence) {
        //定义一个Boolean类型的变量update, 表示是否进行了修改
        Boolean update = false;

        //(1) 使用迭代器遍历currMap
        Iterator<Map.Entry<String, AbstractActivityOperator>> iterator = currMap.entrySet().iterator();

        while (iterator.hasNext()) {

            //拿出相应的状态扭转类实例
            AbstractActivityOperator operator = iterator.next().getValue();

            //(2) 判断Operator是否需要转换
            if (operator.sequence() != sequence || !operator.needConvert(convertActivityStatusDTO)) {
                //如果扭转次序不正确 或 当前实例不需要扭转 --> 则遍历下一个元素
                continue;
            }

            //(3)如果需要扭转 --> 进行状态扭转
            if (!operator.convertStatus(convertActivityStatusDTO)) {
                //如果扭转失败 --> 打印日志并抛出异常
                log.error("{}状态扭转失败！", operator.getClass().getName());
                throw new ServiceException(ServiceErrorCodeConstants.CONVERT_ACTIVITY_STATUS_ERROR);
            }

            //currMap删除当前Operator
            iterator.remove();
            update = true;
        }

        //返回
        return update;
    }
}